
#pragma once

/* 假的层 */
#include <metalc/mlayer/_mlayer.h>
#include <dbg.h>
#include <dinfra/list.h>

#ifndef TCACHE_REQ_NR_ONCE
	#define TCACHE_REQ_NR_ONCE	256
#endif

/* 窃取全局配额 */
#define TCACHE_USE_EXTRA_SIZE		(4 * 1024 * 1024UL)
#define TCACHE_EXTRA_STEP		(1 * 1024 * 1024UL)

#ifndef TCACHE_CACHE_SIZE
	#define TCACHE_CACHE_SIZE	(2UL * 1024 * 1024)
#endif

#define TCACHE_CLEAR_FLISTS_NR_ONCE	(1)
/* 批量释放 */
#define TCACHE_BULK_FREE
#define TCACHE_OOM_ALLOC_ONCE(req)	(((tcache_max_free_size(mtls_by_req(req)->apdstls.tinfo.cid) >> log2_align(TCACHE_REQ_NR_ONCE)) >> log2_align(req->size)) + 1)

#define TCACHE_PRINT(...)		INFO(__VA_ARGS__)

OU2INFUN(cls, tcache_cls)

typedef struct tcache_conf {
	const hword_t total_szcls;
	const hword_t clear_flist_once; /* 释放时一次性最多释放几个队列 */
	const size_t init_max_free_sz;
	
	const size_t min_size;
	const size_t max_sz;
} tcache_conf_t;


SAI word_t tcache_max_free_size(cid_t core_id)
{
	#ifdef TCACHE_SIZEMAPTBL
		return TCACHE_SIZEMAPTBL(core_id);
	#else
		PARAM_USED(core_id);
		return TCACHE_CACHE_SIZE;
	#endif
}

typedef struct
{
	size_t max_free_size;
	size_t free_size;
	/* 接下来是hword数组和slist_t*数组 */
	word_t extra_space[];
	// hword_t cnt[total_szcls];
	// slist_t *flist[total_szcls];
} tcache_obj_t;

SAI size_t tcache_obj_totalsize(const size_t total_szcls){
	return sizeof(tcache_obj_t) + align_up(sizeof(hword_t) * total_szcls, sizeof(word_t)) + sizeof(slist_t *) * 2 * total_szcls;
}
SAI hword_t *tcache_get_fcnts(tcache_obj_t *l){
	return (hword_t *)((paddr_t)l + sizeof(tcache_obj_t));
}
SAI slist_t **tcache_get_flists(tcache_obj_t *l, const size_t total_szcls){
	return (slist_t **)((paddr_t)tcache_get_fcnts(l) + align_up(sizeof(hword_t) * total_szcls, sizeof(word_t)));
}
SAI slist_t **tcache_get_flists_tail(tcache_obj_t *l, const size_t total_szcls){
	return (slist_t **)((paddr_t)tcache_get_flists(l, total_szcls) + sizeof(slist_t *)  * total_szcls);
}
SAI tcache_obj_t *tcache_init(void *mem, size_t size, size_t *pglb_quota, const tcache_conf_t *conf){
	// ASSERT(mem && size >= tcache_obj_totalsize(conf->total_szcls));
	if (!mem || size < tcache_obj_totalsize(conf->total_szcls)) return NULL;
	tcache_obj_t *l = (tcache_obj_t *)mem;
	/* 每申请一个增加全局配额 */
	#ifdef TCACHE_USE_EXTRA_SIZE
	faa(pglb_quota, conf->init_max_free_sz >> 1);
	#else
	PARAM_USED(pglb_quota);
	#endif
	hword_t *cntl = tcache_get_fcnts(l);
	slist_t **flists = tcache_get_flists(l, conf->total_szcls);
	slist_t **flists_tail = tcache_get_flists_tail(l, conf->total_szcls);
	l->max_free_size = conf->init_max_free_sz;
	l->free_size = 0;
	for (hword_t idx = 0; idx < conf->total_szcls; idx++){
		cntl[idx] = 0;
		flists[idx] = (slist_t *)NULL_PTR;
		flists_tail[idx] = (slist_t *)NULL_PTR;
	}
	return l;
}
/* 内存不够 */
SAI size_t tcache_oomhandler(mreq_t *req, size_t size, slist_t **phead, slist_t **ptail, const mlfunc_t * cont){
	size_t nr = TCACHE_OOM_ALLOC_ONCE(req);
	// size_t nr = 1;
	req->sptr = NULL_PTR;
	ASSERT(nr >= 1);
	req->nr = nr;
	req->size = size;
	*phead = (slist_t *)cont->lalloc(req);
	*ptail = (slist_t *)req->sptr;
	// ASSERT(req->nr == nr);
	// ASSERT(slist_len_noloop(*phead) == req->nr);
	TCACHE_PRINT("[tcache] bulk alloc from upper %p to %p, size %lx, nr %lx\n", *phead, *ptail, req->size, req->nr);
	return req->nr;
}

SAI void tcache_clearcls(tcache_obj_t *l, mreq_t *req, szcls_t cls, size_t size, const tcache_conf_t *conf, const mlfunc_t *cont){
	slist_t *head;
	slist_t *tail;
	
	ASSERT(cls < conf->total_szcls);
	
	hword_t *cntl = tcache_get_fcnts(l);
	slist_t **flists_head = tcache_get_flists(l, conf->total_szcls);
	slist_t **flists_tail = tcache_get_flists_tail(l, conf->total_szcls);

	hword_t cnts = cntl[cls];
	head = flists_head[cls];
	tail = flists_tail[cls];

	/* 清空 */
	flists_head[cls] = (slist_t *)NULL_PTR;
	flists_tail[cls] = (slist_t *)NULL_PTR;
	cntl[cls] = 0;
	l->free_size -= cnts * size;

	// size_t real_nr = slist_len_noloop(head);
	// ASSERT(slist_len_noloop(head) == cnts);
	// 向上层释放
	TCACHE_PRINT("[tcache] bulk free to upper %p to %p, size %lx, nr %x\n", head, tail, size, cnts);
	req->size = size;
#ifndef TCACHE_BULK_FREE
	slist_t *cur = head;

	req->tptr = NULL;
	req->free_nr = 0;
	while (cnts-- > 0)
	{
		req->ptr = cur;
		cur = cur->next;
		cont->lfree(req);
	}
#else
	req->ptr = head;
	req->sptr = tail;
	/* 必须赋值，否则失败 */
	req->cls = cls;
	ASSERT(tail->next == NULL_PTR); 
	req->nr = cnts;
	// ASSERT(slist_len_noloop(head) == cnts);
	cont->lfree(req);
#endif
}

SAI hword_t tcache_limitcnt_bysz(cid_t core_id, size_t sz){
	return ((tcache_max_free_size(core_id) >> log2_align(TCACHE_REQ_NR_ONCE)) >> log2_align(sz)) + 1;
}
/* 释放大小超限 */
SAI void tcache_fomhandler(tcache_obj_t *l, mreq_t *req, szcls_t cls, const mlfunc_t *cont, const tcache_conf_t *conf, INFUN(tcache_cls, clspolicy)){
	szcls_t cur_cls = cls;
	hword_t fls = 0;
	hword_t *cntl = tcache_get_fcnts(l);
	cid_t cid = mtls_by_req(req)->apdstls.tinfo.cid;
	do
	{
		size_t size = clspolicy->cls2sz(cur_cls);
		if (//p->_flists.freelists[i].head != NULL && 
		cntl[cur_cls]  > tcache_limitcnt_bysz(cid, size))
		{
			tcache_clearcls(l, req, cur_cls, size, conf, cont);

			if (++fls >= conf->clear_flist_once)
			{
				break;
			}
		}

		cur_cls++;
		if (cur_cls >= conf->total_szcls) cur_cls = 0;
	} while (cur_cls != cls);
}

SAI void _tcache_pinit(const mlfunc_t * cont){
	PARAM_USED(cont);
	TCACHE_PRINT("_tcache pinit\n");
}
SAI void _tcache_tinit(const mlfunc_t * cont, size_t *pquota, perm_allocf_t *alloc, const tcache_conf_t *conf){
	mtls_t *tls = mtls_get();
	ASSERT(tls);
	size_t sz = tcache_obj_totalsize(conf->total_szcls);
	void *mem = alloc(sz);
	ASSERT(mem);
	if (!tls->objs[cont->lidx]) tls->objs[cont->lidx] = tcache_init(mem, sz, pquota, conf);
}

SAI void *_tcache_alloc(mreq_t *req, const mlfunc_t * cont, size_t *pglb_quota, const tcache_conf_t *conf, INFUN(tcache_cls, clspolicy)){
	
	size_t size = req->size;
	
	/* 对齐 */
	size_t alignment = req->align;
	if (UNLIKELY(alignment != 0)){
		/* 对齐 */
		size = align_up(size, alignment);
	}

	if (__MLAYER_INVALID_REQSZ(conf->max_sz, size)) return cont->lalloc(req);

	mtls_t *tls = mtls_by_req(req);
	tcache_obj_t *tcache = (tcache_obj_t *)MLAYER_GET_OBJ(tls, cont);
	ASSERT(tcache);
	// size_t size = align_up(req->size, conf->min_size);
	/* 自对齐只需要按此对齐即可 */
	// size = align_up(req->size, alignment);
	szcls_t cls = clspolicy->sz2cls(size);
	size_t aligned_size = clspolicy->cls2sz(cls);
	
	ASSERT(aligned_size >= conf->min_size);
	
	hword_t *cntl = tcache_get_fcnts(tcache);
	slist_t **flists_head = tcache_get_flists(tcache, conf->total_szcls);
	slist_t **flists_tail = tcache_get_flists_tail(tcache, conf->total_szcls);

	void *ret = flists_head[cls];
	if (ret) {
		/* pop成功，检查 */
		flists_head[cls] = flists_head[cls]->next;
		cntl[cls] -= 1;
		if (cntl[cls] == 0){
			ASSERT(flists_head[cls] == NULL_PTR);
			flists_tail[cls] = (slist_t *)NULL_PTR;
		}
		tcache->free_size -= aligned_size;
	}
	else{
		ASSERT(flists_head[cls] == NULL_PTR);
		ASSERT(flists_tail[cls] == NULL_PTR);

		#ifdef TCACHE_USE_EXTRA_SIZE
		/* 归还额度? */
		/* 更新coreID */
		apdstls_upd();
		if (tcache->max_free_size > tcache_max_free_size(tls->apdstls.tinfo.cid))
		{
			faa(pglb_quota, TCACHE_EXTRA_STEP);
			/* 归还额度 */
			tcache->max_free_size -= TCACHE_EXTRA_STEP;
		}
		#endif
		/* 尝试获得 */
		slist_t *head;
		slist_t *tail;
		size_t blknr = tcache_oomhandler(req, size, &head, &tail, cont);
		ret = (void *)head;
		if (!ret) goto RET;
		/* ret存在 */
		tcache->free_size += (blknr - 1) * aligned_size;
		
		// ASSERT(slist_len_noloop(head->next) == blknr - 1);
		if (blknr - 1 > 0){
			ASSERT(tail);
			ASSERT(head != tail);
			// ASSERT(slist_len_noloop(head->next) == blknr - 1);
			// tail->next = flists_head[cls];
			ASSERT(tail->next == NULL_PTR);
			ASSERT(cntl[cls] == 0);
			flists_tail[cls] = tail;
			/* ASSERT HD tail是连接的 */
			ASSERT(head->next);
			flists_head[cls] = head->next;
			cntl[cls] += blknr - 1;
		}
	}

RET:
	req->size = aligned_size;
	req->ptr = ret;
	ASSERT(aligned_size >= size);
	TCACHE_PRINT("[tcache] alloc %p, rqsize %lx, real_size %lx, tcache %p, cur free size %lu\n", ret, size, aligned_size, tcache, tcache->free_size);
	return ret;
}
SAI bool_t _tcache_free(mreq_t *req, const mlfunc_t * cont, size_t *pglb_quota, const tcache_conf_t *conf, INFUN(tcache_cls, clspolicy)){
	size_t size = req->size;
	TCACHE_PRINT("[tcache] try free %p, size %lx\n", req->ptr, size);
	if(UNLIKELY(MLAYER_INVALID_REQSZ(cont, size))){
		ASSERT(req->nr == 1);
		return cont->lfree(req);
	}
	mtls_t *tls = mtls_by_req(req);
	tcache_obj_t *tc = (tcache_obj_t *)MLAYER_GET_OBJ(tls, cont);
#ifdef TCACHE_UPPER_NOT_EXACT_SIZE
	szcls_t cls = clspolicy->sz2cls(size);
	size_t aligned_size = clspolicy->cls2sz(cls);

	/* 如果上层和本层并非相同大小阶策略，可能会造成问题，得到的大小需要降阶*/
	/* 降阶 */
	if (size < aligned_size) cls -= 1;
	aligned_size = clspolicy->cls2sz(cls);
#else
	/* 刚好准确大小，同时省去转换开销 */
	szcls_t cls = req->cls;
	size_t aligned_size = size;
#endif
	ASSERT(size >= aligned_size);
	
	hword_t *cntl = tcache_get_fcnts(tc);
	slist_t **flists_head = tcache_get_flists(tc, conf->total_szcls);
	slist_t **flists_tail = tcache_get_flists_tail(tc, conf->total_szcls);
	slist_t *node = (slist_t *)req->ptr;
	ASSERT(node);
	hword_t cnt = cntl[cls] + 1;
	
	cntl[cls] = cnt;
	node->next = flists_head[cls];
	if (cnt == 1) {
		ASSERT(flists_tail[cls] == NULL_PTR);
		flists_tail[cls] = node;
	}
	flists_head[cls] = node;
	
	// size_t real_cnt = slist_len_noloop(node);
	// ASSERT(slist_len_noloop(node) == cnt);
	tc->free_size += aligned_size;
	TCACHE_PRINT("[tcache] freed %p, size %lx, cur free size %ld\n", req->ptr, size, tc->free_size);
RETRY:
	if (tc->free_size > tc->max_free_size) {
#ifdef TCACHE_USE_EXTRA_SIZE
		/* 尝试窃取全局配额阻止 */
		size_t k = atm_ld(pglb_quota);
		if (k >= TCACHE_EXTRA_STEP){
			if (cas(pglb_quota, &k, k - TCACHE_EXTRA_STEP)){
				tc->max_free_size += TCACHE_EXTRA_STEP;
				goto RETRY;
			}
		}
#endif
		/* 应该从最不常用的cls入手 */
		tcache_fomhandler(tc, req, cls, cont, conf, clspolicy);
		TCACHE_PRINT("[tcache] cleaned, rest free_size %ld\n", tc->free_size);
	}

	return TRUE;
}
SAI size_t _tcache_sizeof(mreq_t *req, const mlfunc_t * cont){
	size_t sz = cont->lsizeof(req);
	TCACHE_PRINT("[tcache] sizeof %p, size %lx\n", req->ptr, sz);
	return sz;
	
}

/* 定义 
 * min_bit - 管理的最小bit位
 * max_bit - 管理的最大bit位
 * cls_comp - szcls策略
 * init_glb_quotasz - 初始时的全局配额大小(推荐为4MB)
 * init_max_quota - 每个线程初始化时的私有配额(推荐为2MB)
 * clear_flist_once_nr - 当线程配私有额超限时一次性释放的cls链表数目(推荐为 2)
 * perm_alloc - 永久对象分配函数
*/
#define DEF_TCACHE_MLAYER(name, min_bit, max_bit, cls_comp, init_glb_quotasz, init_max_quota, clear_flist_once_nr, perm_alloc)\
DEF_MLAYER_BASIC(name, BIT(max_bit), 1)\
static const tcache_conf_t name##_conf = { .total_szcls = _clsnr_##cls_comp, .clear_flist_once = clear_flist_once_nr, \
.init_max_free_sz = init_max_quota, .min_size = BIT(min_bit), .max_sz = _maxsz_##cls_comp};\
static size_t name##_glb_quota_sz = init_glb_quotasz;\
SAI void name##_pinit(const mlfunc_t * cont){ return _tcache_pinit(cont); }\
SAI void name##_tinit(const mlfunc_t * cont){ return _tcache_tinit(cont, &name##_glb_quota_sz, perm_alloc, &name##_conf); }\
SAI void *name##_alloc(mreq_t *req, const mlfunc_t * cont){\
	return _tcache_alloc(req, cont, &name##_glb_quota_sz, &name##_conf, OUFUN_OF(cls_comp));\
}\
SAI bool_t name##_free(mreq_t *req, const mlfunc_t * cont){\
	return _tcache_free(req, cont, &name##_glb_quota_sz, &name##_conf, OUFUN_OF(cls_comp));\
}\
SAI size_t name##_sizeof(mreq_t *req, const mlfunc_t * cont){\
	return _tcache_sizeof(req, cont);\
}
